﻿#region Usings

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using EFCachingProvider.Caching;
using Microsoft.Practices.Unity;
using XPressify.Data.Helpers;
using XPressify.Helpers;
using XPressify.Models;
using XPressify.Controllers;
using XPressify.Data;

#endregion

namespace XPressify.Controllers
{
    public class UserController : SubjectController
    {
        public ActionResult LogOn()
        {
            return View();
        }

        [HttpPost]
        public ActionResult LogOn(LogOnModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                // TODO: see the tow lines bellow
                Repository.Cache = HttpContext.Cache["Cache"] as ICache;
                Repository.CachingPolicy = HttpContext.Cache["Caching_Policy"] as CachingPolicy;

                var user =
                    Repository.AsQueryable<User>().SingleOrDefault(userDb => userDb.Email == model.Email);
                if (user != null)
                {
                    if (user.Password.Equals(model.Password))
                    {
                        if (user.Status == 0)
                        {
                            return RedirectToAction("Pending", user.Id);
                        }


                        var authTicket = new FormsAuthenticationTicket(1, model.Email, DateTime.Now,
                                                                       DateTime.Now.AddMinutes(30), model.RememberMe,
                                                                       user.SerializeForTicket());
                        var encTicket = FormsAuthentication.Encrypt(authTicket);
                        Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));

                        user.LastLogin = DateTime.Now;
                        Repository.SaveChanges();
                        return RedirectToAction("NewsFeed", "Home");
                    }
                    ModelState.AddModelError("Invalid_Password", "Sorry. Invalid password!");
                }
                else
                    ModelState.AddModelError("Invalid_Username", "Sorry. Invalid username!");
            }
            return View(model);
        }

        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();

            return RedirectToAction("Index", "Home");
        }

        public ActionResult Register()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                var valid = true;

                // validate the model
                if (Repository.AsQueryable<User>().Any(x => x.Email == model.Email))
                {
                    ModelState.AddModelError("Email", "Email already exists");
                    valid = false;
                }

                if (!String.IsNullOrWhiteSpace(model.Password))
                {
                    if (model.Password != model.ConfirmPassword)
                    {
                        ModelState.AddModelError("ConfirmPassword", "Passwords don't match");
                        valid = false;
                    }

                    if (model.Password.Length < 5)
                    {
                        ModelState.AddModelError("Password", "Password minimum length is 5 characters");
                        valid = false;
                    }
                }

                if (valid)
                {
                    // if the model is valid, save the user to the db and send an activation email
                    var user = new User
                                   {
                                       Id = Guid.NewGuid(),
                                       FirstName = model.FirstName,
                                       LastName = model.LastName,
                                       Gender = model.Gender,
                                       Birthday = model.Birthday,
                                       Email = model.Email,
                                       Password = model.Password,
                                       Created = DateTime.Now,
                                       Type = SubjectType.User,
                                       PrivacyType = SubjectPrivacy.Public,
                                       Role = UserRole.User,
                                       Status = UserStatus.Pending,
                                       Username = ""
                                   };
                    Repository.AddObject(user);
                    Repository.SaveChanges();

                    SendEmailConfirmRegistration(user);

                    return ActivationEmailSent();
                }
            }
            return View(model);
        }

        public virtual ActionResult Activate(Guid id)
        {
            var user = Repository.AsQueryable<User>().FirstOrDefault(x => x.Id == id);
            if (user == null)
                return NotFound();

            user.Status = UserStatus.Actived;

            SendEmailAccountActivated(user);

            var wallAlbum = new Album
            {
                BaseSubject = user,
                Created = DateTime.Now,
                Id = Guid.NewGuid(),
                Description = "",
                Location = "",
                Name = "Wall album",
                Type = ObjectType.Album,
                PrivacyType = ObjectPrivacy.FriendsOnly,
                MediaType = AlbumMediaType.Mix,
                IsWall = true,
                IsProfile = false
            };
            var wallAblumPostDummy = new Post
            {
                BaseObject = wallAlbum,
                BaseSubject = wallAlbum.BaseSubject,
                Content = "",
                Id = Guid.NewGuid(),
                Description = "Post dummy - wall album",
                Created = DateTime.Now,
                Title = "Post dummy - wall album",
                PostType = PostType.PostDummy,
                Type = BaseMessageType.Post,
                CommentType = CommentType.PostWithItsComments
            };
            var profileAlbum = new Album
            {
                BaseSubject = user,
                Created = DateTime.Now,
                Id = Guid.NewGuid(),
                Description = "",
                Location = "",
                Name = "Profile Album",
                Type = ObjectType.Album,
                PrivacyType = ObjectPrivacy.Everyone,
                MediaType = AlbumMediaType.Photo,
                IsWall = false,
                IsProfile = true
            };
            var profileAlbumPostDummy = new Post()
            {
                BaseObject = profileAlbum,
                BaseSubject = profileAlbum.BaseSubject,
                Id = Guid.NewGuid(),
                Created = DateTime.Now,
                Title = "Post dummy - profile album",
                Type = BaseMessageType.Post,
                PostType = PostType.PostDummy,
                Description = "Post dummy - profile album",
                Content = "",
                CommentType = CommentType.PostWithGlobalComments
            };

            // create a default profile picture media
            var newProfile = new Media()
            {
                Id = Guid.NewGuid(),
                Album = profileAlbum,
                BaseSubject = user,
                Created = DateTime.Now,
                Description = "",
                FileType = "jpg",
                IsProfile = true,
                IsCover = false, // cannot be cover of profile album
                MediaType = MediaType.Photo,
                Name = "Picture",
                PrivacyType = profileAlbum.PrivacyType, // set a privacy of album
                Type = ObjectType.Media
            };

            var mediaPostDummy = new Post()
            {
                BaseObject = newProfile,
                BaseSubject = newProfile.BaseSubject,
                Content = "",
                Id = Guid.NewGuid(),
                Created = DateTime.Now,
                Type = BaseMessageType.Post,
                Title = "Post dummy - media",
                Description = "Post dummy - media",
                PostType = PostType.PostDummy,
                CommentType = CommentType.PostWithItsComments
            };

            Repository.AddObject(wallAlbum);
            Repository.AddObject(profileAlbum);
            Repository.AddObject(newProfile);
            Repository.AddObject(wallAblumPostDummy);
            Repository.AddObject(profileAlbumPostDummy);
            Repository.AddObject(mediaPostDummy);

            Repository.SaveChanges();




            var path = Server.MapPath("~/Media/" + user.Id + "/"); // create a user directory
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path); // create directory
            }

            path = Server.MapPath("~/Media/" + user.Id + "/" + wallAlbum.Id + "/"); // create a wall directory
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path); // create directory
            }

            path = Server.MapPath("~/Media/" + user.Id + "/" + profileAlbum.Id + "/"); // create a profile directory
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path); // create directory
            }

            //copy a default profile picture physical file to the profile albume
            var defaultProfilePicture = Server.MapPath("/Media/Image/DefaultProfile.jpg");
            var newProfilePicturePath = Server.MapPath("~/Media/" + user.Id + "/" + profileAlbum.Id + "/" + "ProfilePicture");
            var fileInfo = new FileInfo(defaultProfilePicture);
            fileInfo.CopyTo(newProfilePicturePath);

            return Activated();
        }

        [Authorize]
        public ActionResult ChangePassword()
        {
            return View();
        }

        [Authorize]
        [HttpPost]
        public ActionResult ChangePassword(ChangePasswordModel model)
        {
            var user = CurrentUser;
            var valid = true;
            if (CurrentUser.Password != model.OldPassword)
            {
                ModelState.AddModelError("OldPassword", "Old password is not correct");
                valid = false;
            }
            if (model.NewPassword != model.ConfirmPassword)
            {
                ModelState.AddModelError("NewPassword", "New password and confirm password don't match");
                valid = false;
            }
            if (!ModelState.IsValid || !valid)
            {
                return View(model);
            }

            user.Password = model.NewPassword;
            Repository.SaveChanges();
            return ChangePasswordSuccess();
        }

        public ActionResult ForgotPassword()
        {
            return View();
        }

        [HttpPost]
        public ActionResult ForgotPassword(ForgotPasswordModel model)
        {
            if (ModelState.IsValid)
            {
                var user = Repository.AsQueryable<User>().SingleOrDefault(x => x.Email == model.Email);
                if (user != null)
                {
                    SendEmailForgotPassword(user);
                    return ForgotPasswordEmailSent();
                }

                ModelState.AddModelError("Email",
                                         "No user was found with this email address. Please try another email address.");
            }

            return View(model);
        }

        public ActionResult AddFriend(Guid id)
        {
            var currentUser = CurrentUser;

            // find the friend
            var newFriend = Repository.AsQueryable<User>().SingleOrDefault(sub => sub.Id == id);
            if (newFriend != null)
            {
                var previousRelationship =
                    Repository.AsQueryable<Relationship>().SingleOrDefault(
                        re =>
                        ((re.BaseSubject.Id == currentUser.Id && re.BaseSubject2.Id == newFriend.Id) ||
                         (re.BaseSubject.Id == newFriend.Id && re.BaseSubject2.Id == currentUser.Id)) &&
                        re.RelationType == RelationshipType.FriendOf);
                if(previousRelationship != null) // if already exists then we cant add them again
                {
                    return RedirectToAction("ProfileView", "Profile", new {id = currentUser.Id });
                }
                // create new relationship with status "requested"
                var rel = new Relationship
                              {
                                  Id = Guid.NewGuid(),
                                  BaseSubject = currentUser,
                                  BaseSubject2 = newFriend,
                                  Created = DateTime.Now,
                                  RelationType = RelationshipType.FriendOf,
                                  Type = ObjectType.Relationship,
                                  Status = RelationshipStatus.Requested,
                                  PrivacyType = ObjectPrivacy.FriendsOnly,
                                  Description = RelationshipType.FriendOfText,
                                  Name = RelationshipType.FriendOfText
                              };
                Repository.AddObject(rel);

                //make notification
                var newNotification = new Notification()
                {
                    Active = true,
                    BaseSubject = rel.BaseSubject2,
                    Created = DateTime.Now,
                    Id = Guid.NewGuid(),
                    Type = BaseMessageType.Notification
                };
                var viewData2 = new
                {
                    UserId = rel.BaseSubject.Id,
                    UserFullname = rel.BaseSubject.FullName,
                    RelationshipType = "friend"
                };
                var viewDictionary2 = viewData2.ToViewDataDictionary();
                newNotification.Content = ControlToString("~/Views/Notification/RequstedRelationshipWithYou.cshtml", viewDictionary2);
                Repository.AddObject(newNotification);

                Repository.SaveChanges();

                SendEmailConfirmFriendship(rel);
                return AddFriendEmailSent();
            }
            return UserNotFound();
        }

        public ActionResult RemoveFriend(Guid id)
        {
            var currentUser = CurrentUser;


            var relationship =
                Repository.AsQueryable<Relationship>().SingleOrDefault(
                    rel =>
                    ((rel.BaseSubject.Id == currentUser.Id && rel.BaseSubject2.Id == id) ||
                     (rel.BaseSubject2.Id == currentUser.Id && rel.BaseSubject.Id == id)) &&
                    rel.RelationType == RelationshipType.FriendOf);
            
            if (relationship != null)
            {
                // remove the relationship
                Repository.DeleteObject(relationship);
                
                //delete all postLikes && post connected with relationship
                var allPostLike = Repository.AsQueryable<PostLike>().Where(pst => pst.Post.BaseObject.Id == relationship.Id);
                foreach (var postLike in allPostLike)
                {
                    Repository.DeleteObject(postLike);
                }
                var allPost = Repository.AsQueryable<Post>().Where(pst => pst.BaseObject.Id == relationship.Id);
                foreach (var post in allPost)
                {
                    Repository.DeleteObject(post);
                }


                // find all other relationships with this friend
                var allRelationship =
                Repository.AsQueryable<Relationship>().Where(
                    rel =>
                    ((rel.BaseSubject.Id == currentUser.Id && rel.BaseSubject2.Id == id) ||
                     (rel.BaseSubject2.Id == currentUser.Id && rel.BaseSubject.Id == id)));

                // and delete them
                foreach (var relationship1 in allRelationship)
                {

                    //delete all postLikes && post connected with relationship1
                    allPostLike = Repository.AsQueryable<PostLike>().Where(pst => pst.Post.BaseObject.Id == relationship1.Id);
                    foreach (var postLike in allPostLike)
                    {
                        Repository.DeleteObject(postLike);
                    }
                    allPost = Repository.AsQueryable<Post>().Where(pst => pst.BaseObject.Id == relationship1.Id);
                    foreach (var post in allPost)
                    {
                        Repository.DeleteObject(post);
                    }

                    //delete relationship
                    Repository.DeleteObject(relationship1);
                }
                Repository.SaveChanges();

                // notify
                var model = new NotifyModel
                {
                    Title = "XPressify - Remove friend",
                    Header = "Remove Friend",
                    Body = "Friend removed successfully",
                    NotificationType = NotificationType.Notification
                };
                return RedirectToAction("Notify", "Home", model);
            }
            return UserNotFound();
        }

        public ActionResult BlockUser(Guid id)
        {
            var currentUser = CurrentUser;

            // find the relationship with this friend
            var relationship =
                Repository.AsQueryable<Relationship>().SingleOrDefault(
                    rel =>
                    ((rel.BaseSubject.Id == currentUser.Id && rel.BaseSubject2.Id == id) ||
                     (rel.BaseSubject2.Id == currentUser.Id && rel.BaseSubject.Id == id)) && rel.RelationType == RelationshipType.FriendOf);

            // if the relationship exists, set the status to "blocked", otherwice create one wiht status "blocked"
            if (relationship != null)
            {
                if(relationship.BaseSubject.Id != currentUser.Id && relationship.BaseSubject2.Id == currentUser.Id) //if current user is not in first column then we must swap it to make it to first, so it can look like this, eg. currentUser blocked otherFriend
                {
                    relationship.BaseSubject2 = relationship.BaseSubject;
                    relationship.BaseSubject = currentUser;
                }
                relationship.Status = RelationshipStatus.Blocked;
            }
            else
            {
                var userToBeBlocked = Repository.AsQueryable<User>().SingleOrDefault(usr => usr.Id == id);
                var newRelationship = new Relationship
                                          {
                                              BaseSubject = currentUser,
                                              BaseSubject2 = userToBeBlocked,
                                              Created = DateTime.Now,
                                              Name = "Block User Relationship",
                                              PrivacyType = ObjectPrivacy.FriendsOnly,
                                              Type = ObjectType.Relationship,
                                              Description = "Block User Relationship",
                                              Status = RelationshipStatus.Blocked,
                                              RelationType = RelationshipType.FriendOf,
                                              Id = Guid.NewGuid()
                                          };
                Repository.AddObject(newRelationship);
            }
            Repository.SaveChanges();
            // find all other relationships with this friend
            var allRelationship = 
                Repository.AsQueryable<Relationship>().Where(
                    rel =>
                    ((rel.BaseSubject.Id == currentUser.Id && rel.BaseSubject2.Id == id) ||
                     (rel.BaseSubject2.Id == currentUser.Id && rel.BaseSubject.Id == id)) && rel.Status != RelationshipStatus.Blocked);

            // delete them all
            foreach (var relationship1 in allRelationship)
            {

                //delete all postLikes && post connected with relationship1
                var allPostLike = Repository.AsQueryable<PostLike>().Where(pst => pst.Post.BaseObject.Id == relationship1.Id);
                foreach (var postLike in allPostLike)
                {
                    Repository.DeleteObject(postLike);
                }
                var allPost = Repository.AsQueryable<Post>().Where(pst => pst.BaseObject.Id == relationship1.Id);
                foreach (var post in allPost)
                {
                    Repository.DeleteObject(post);
                }

                Repository.DeleteObject(relationship1);
            }

            Repository.SaveChanges();

            // notify
            var model = new NotifyModel
            {
                Title = "XPressify - Block user",
                Header = "Block user",
                Body = "Friend blocked successfully",
                NotificationType = NotificationType.Notification
            };
            return RedirectToAction("Notify", "Home", model);
        }

        public ActionResult ResendActivationEmail(Guid id)
        {
            var user = Repository.AsQueryable<User>().SingleOrDefault(x => x.Id == id);
            SendEmailConfirmRegistration(user);

            return ActivationEmailSent();
        }

        #region Notify functions

        public ActionResult ActivationEmailSent()
        {
            var model = new NotifyModel
                            {
                                Title = "XPressify - Activation email sent",
                                Header = "Activation email has been sent to your email address",
                                Body =
                                    "Please check your email box and activate your account by clicking on the activation link.",
                                NotificationType = NotificationType.Notification
                            };
            return RedirectToAction("Notify", "Home", model);
            //return View();
        }

        
        public ActionResult AddFriendEmailSent()
        {
            var model = new NotifyModel
                            {
                                Title = "Xpressify - Email sent",
                                Header = "Email sent",
                                Body = "Email successfully sent.",
                                NotificationType = NotificationType.Notification
                            };


            return RedirectToAction("Notify", "Home", model);
        }

        public ActionResult Activated()
        {
            var model = new NotifyModel
                            {
                                Title = "Xpressify - Account successfully activated",
                                Header = "Account successfully activated",
                                Body = "Please login with your account",
                                NotificationType = NotificationType.Notification
                            };


            return RedirectToAction("Notify", "Home", model);
        }

        public ActionResult Pending(Guid id)
        {
            var user = Repository.AsQueryable<User>().SingleOrDefault(x => x.Id == id);

            var routeValues = new Dictionary<string, object> {{"id", user.Id}};
            var resendLink = HtmlHelper.GenerateLink(Request.RequestContext, RouteTable.Routes, "resend", "Default",
                                                     "ResendActivationEmail", "User",
                                                     new RouteValueDictionary(routeValues), null);

            var model = new NotifyModel
                            {
                                Title = "Xpressify - Account activation pending",
                                Header = "Account activation is pending",
                                Body =
                                    "Please check for account activation email in you email box and activate your account. " +
                                    "Or if you haven't received an email yet, try to " + resendLink +
                                    "an activation email again.",
                                NotificationType = NotificationType.Notification
                            };

            return RedirectToAction("Notify", "Home", model);
        }

        public ActionResult NotFound()
        {
            var model = new NotifyModel
                            {
                                Title = "Xpressify - Activation failed.",
                                Header = "Activation failed. User not found",
                                Body = "User was not found. Please try to register again.</p>",
                                NotificationType = NotificationType.Warning
                            };
            return RedirectToAction("Notify", "Home", model);
        }

        public ActionResult UserNotFound()
        {
            var model = new NotifyModel
                            {
                                Title = "Xpressify - User Not Found",
                                Header = "User was not found in database",
                                Body = "User is not correct or doesn't exist",
                                NotificationType = NotificationType.Warning
                            };
            return RedirectToAction("Notify", "Home", model);
        }


        public ActionResult ChangePasswordSuccess()
        {
            var model = new NotifyModel
                            {
                                Title = "Xpressify - Change Password",
                                Header = "Change Password",
                                Body = "Your password has been changed successfully.",
                                NotificationType = NotificationType.Notification
                            };
            return RedirectToAction("Notify", "Home", model);
        }

        public ActionResult ForgotPasswordEmailSent()
        {
            var model = new NotifyModel
                            {
                                Title = "Xpressify - Password reminder email sent",
                                Header = "Password reminder email has been sent to your email address",
                                Body = "Please check your email box and login to your account.",
                                NotificationType = NotificationType.Notification
                            };
            return RedirectToAction("Notify", "Home", model);
        }
        #endregion

        #region Subject members

        public override ActionResult ConfirmRelationshipNotification(Relationship relationship)
        {
            var model = new NotifyModel
                            {
                                Title = "Friend request",
                                Header = "Confirm friend",
                                Body = "Friend request succesfully confirmed.",
                                NotificationType = NotificationType.Notification
                            };

            return RedirectToAction("Notify", "Home", model);
        }

        public override ActionResult PostponeRelationshipNotification(Relationship relationship)
        {
            var model = new NotifyModel
                            {
                                Title = "Friend request",
                                Header = "Pospone friend",
                                Body = "Friend request succesfully postponed.",
                                NotificationType = NotificationType.Notification
                            };

            return RedirectToAction("Notify", "Home", model);
        }

        public override ActionResult PostponeFailedRelationshipNotification(Relationship relationship)
        {
            var model = new NotifyModel
                            {
                                Title = "Friend request",
                                Header = "Pospone friend",
                                Body =
                                    "Fried request couldn't be postponed, because the friendship was already confrirmed.",
                                NotificationType = NotificationType.Notification
                            };

            return RedirectToAction("Notify", "Home", model);
        }

        #endregion

        #region Email sending

        public bool SendEmailConfirmRegistration(User user)
        {
            var viewData = new
                               {
                                   XSettings.SiteName,
                                   user.Password,
                                   ActivationUrl = new Uri(HttpContext.Request.Url, Url.Action("Activate", "User", new {user.Id})).ToString(),
                                   XSettings.SiteUrl
                               };

            var viewDictionary = viewData.ToViewDataDictionary();

            // send an activation email
            return SendTemplatedEmail(new List<string> {user.Email}, String.Format("{0} site: Account confirmation is required", XSettings.SiteName),
                                                   "~/Views/Email/ConfirmRegistration.cshtml", viewDictionary);
        }

        public bool SendEmailConfirmFriendship(Relationship rel)
        {
            var user = CurrentUser;
            var viewData = new
                               {
                                   XSettings.SiteName,
                                   YourName = user.FullName,
                                   FriendName = rel.BaseSubject.FullName,
                                   RelationshipId = rel.Id,
                                   ConfirmFriendshipUrl =
                                       new Uri(HttpContext.Request.Url,
                                               Url.Action("ConfirmRelationship", "User", new {id = rel.Id})).ToString(),
                                   PostponeFriendshipUrl =
                                       new Uri(HttpContext.Request.Url,
                                               Url.Action("PostponeRelationship", "User", new {id = rel.Id})).ToString(),
                                   YourProfileUrl =
                                       new Uri(HttpContext.Request.Url,
                                               Url.Action("ProfileView", "Profile", new { id = user.Id })).ToString(),
                                   XSettings.SiteUrl
                               };
            var viewDictionary = viewData.ToViewDataDictionary();

            return SendTemplatedEmail(new List<string> {((User) rel.BaseSubject2).Email}, String.Format("{0} site: Friendship confirmation is required", XSettings.SiteName),
                                                   "~/Views/Email/ConfirmFriendship.cshtml", viewDictionary);
        }

        public bool SendEmailAccountActivated(User user)
        {
            var viewData = new
                               {
                                   XSettings.SiteName,
                                   user.Password,
                                   XSettings.SiteUrl
                               };

            var viewDictionary = viewData.ToViewDataDictionary();

            // send an activation email
            return SendTemplatedEmail(new List<string> {user.Email}, String.Format("{0} site: Account successfully activated", XSettings.SiteName),
                                                   "~/Views/Email/AccountActivated.cshtml", viewDictionary);
        }

        public bool SendEmailForgotPassword(User user)
        {
            var viewData = new
                               {
                                   XSettings.SiteName,
                                   user.Password,
                                   XSettings.SiteUrl
                               };

            var viewDictionary = viewData.ToViewDataDictionary();

            // send an activation email
            return SendTemplatedEmail(new List<string> {user.Email}, String.Format("{0} site: Password reminder", XSettings.SiteName),
                                                   "~/Views/Email/ForgotPassword.cshtml", viewDictionary);
        }

        #endregion
    }
}